#include "stdafx.h"

#include <time.h>
#include <stdio.h>
#include <stdarg.h>

#include "Config.h"
#include "ComHook.h"

#define HELENA_CUSTOMIMPORT_REGISTERY_KEY "SOFTWARE\\Helena\\Custom Importer"

HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
LPWriteFile g_lpWriteFileForLogging = NULL;

BOOL g_bLoggingEnabled;
BOOL g_bLogDetails;
char g_strInstallationDirectory[1024];
char g_strDatabaseConnectionString[1024];

BOOL CreateLogFile(LPCSTR strLoggingDirectory)
{
	try
	{
		g_lpWriteFileForLogging = (LPWriteFile)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "WriteFile");

		char strFilename[1024];
		strcpy(strFilename, strLoggingDirectory);
		int iTail = strlen(strFilename);
		if (iTail > 1 && strFilename[iTail - 1] != '\\') { strFilename[iTail++] = '\\'; strFilename[iTail] = '\0'; }

		time_t now = time(NULL);
		tm tmTime = *(localtime(&now));
		strftime(strFilename + iTail, 20, "%Y%m%d_%H%M%S.LOG", &tmTime);

		g_hLogFile = CreateFileA(strFilename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);

		if (g_hLogFile == INVALID_HANDLE_VALUE)
			return SetInitialisationFatalError("Failed creating logging file");
	}
	catch (...)
	{
		return SetInitialisationFatalError("Unhandled exception while creating log file");
	}
}

void CloseLogFile()
{
	CloseHandle(g_hLogFile);
	g_hLogFile = INVALID_HANDLE_VALUE;
}

BOOL Log(LPCSTR message, ...)
{
	try
	{
		if (g_bLoggingEnabled && g_hLogFile != INVALID_HANDLE_VALUE && g_lpWriteFileForLogging != NULL)
		{
			va_list pArg;
			va_start(pArg, message);
			char buf[2048];
			_vsnprintf(buf, 2048, message, pArg);
			va_end(pArg);

			DWORD dwBytesWritten;
			if (FALSE == g_lpWriteFileForLogging(g_hLogFile, buf, strlen(buf), &dwBytesWritten, NULL))
				return SetInitialisationFatalError("Unhandled Exception while logging");
			if (FALSE == g_lpWriteFileForLogging(g_hLogFile, "\r\n", 2, &dwBytesWritten, NULL))
				return SetInitialisationFatalError("Unhandled Exception while logging");
			if (FALSE == FlushFileBuffers(g_hLogFile))
				return SetInitialisationFatalError("Unhandled Exception while logging");

			return TRUE;
		}
	}
	catch (...)
	{
		return SetInitialisationFatalError("Unhandled Exception while logging");
	}
}

BOOL LogWin32Error(LPCSTR message, ...)
{
	try
	{
		DWORD dwError = GetLastError();

		va_list pArg;
		va_start(pArg, message);
		char buf[2048];
		_vsnprintf(buf, 2048, message, pArg);
		va_end(pArg);

		if (!Log(buf))
			return FALSE;

		char errmsg[1024];
		if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, dwError, 0, errmsg, sizeof(errmsg), NULL))
			return Log("Windows Error [0x%X]: %s", dwError, errmsg);
		else
			return Log("Windows Error [0x%X]: <Couldn't retreive win32 error message>", dwError);
	}
	catch (...)
	{
		return SetInitialisationFatalError("Unhandled Exception while logging");
	}
}

LPCSTR GetProcessName()
{
	return GetModuleName(GetModuleHandle(NULL));
}

LPCSTR GetModuleName(HMODULE hModule)
{
	try
	{
		char filename[1024];
		if (!GetModuleFileNameA(hModule, filename, sizeof(filename)))
			return NULL;
		return strrchr(filename, '\\') + 1;

		/*char* filenameStart;

		for (filenameStart = filename; *filenameStart != 0; filenameStart++) ;
		for (; *filenameStart != '\\'; filenameStart--) ;
		return filenameStart + 1;*/
	}
	catch (...)
	{
		SetInitialisationFatalError("Unhandled Exception while getting module name");
		return NULL;
	}
}

BOOL SetInitialisationFatalError(LPCSTR strMessage)
{
	HKEY hKey;
    if (ERROR_SUCCESS == RegOpenKeyExA(HKEY_CURRENT_USER, HELENA_CUSTOMIMPORT_REGISTERY_KEY, 0, KEY_ALL_ACCESS , &hKey))
	{
		RegSetValueExA(hKey, "Last Fatal Error", 0, REG_SZ, (LPBYTE)strMessage, strlen(strMessage));
		RegCloseKey(hKey);
	}

	return FALSE;
}

BOOL ReadConfiguration()
{
	char strText[1024]; BYTE bytBoolean;
	HKEY hKey;
	DWORD dwBinaryType = RRF_RT_REG_BINARY;
	DWORD dwStringType = RRF_RT_REG_SZ;

	if (ERROR_SUCCESS != RegOpenKeyExA(HKEY_CURRENT_USER, HELENA_CUSTOMIMPORT_REGISTERY_KEY, 0, KEY_READ, &hKey))
		return FALSE;

	DWORD dwBufferSize = sizeof(bytBoolean);
	if (ERROR_SUCCESS != RegQueryValueExA(hKey, "Use Custom Import", NULL, &dwBinaryType, &bytBoolean, &dwBufferSize))
	{
		RegCloseKey(hKey);
		return FALSE;
	}
	if (bytBoolean == 0)
	{
		RegCloseKey(hKey);
		return FALSE;
	}

	dwBufferSize = sizeof(strText);
	if (ERROR_SUCCESS != RegQueryValueExA(hKey, "Hook Target", NULL, &dwStringType, (LPBYTE)strText, &dwBufferSize))
	{
		RegCloseKey(hKey);
		return FALSE;
	}
	char strProcessName[128];
	strcpy(strProcessName, GetProcessName());
	if (!IsStringsEqualIgnoreCase(strProcessName, strText))
	{
		RegCloseKey(hKey);
		return FALSE;
	}

	dwBufferSize = sizeof(g_strInstallationDirectory);
	if (ERROR_SUCCESS != RegQueryValueExA(hKey, "Installation Directory", NULL, &dwStringType, (LPBYTE)g_strInstallationDirectory, &dwBufferSize))
	{
		RegCloseKey(hKey);
		return FALSE;
	}

	dwBufferSize = sizeof(bytBoolean);
	if (ERROR_SUCCESS != RegQueryValueExA(hKey, "Logging Enabled", NULL, &dwBinaryType, &bytBoolean, &dwBufferSize))
	{
		RegCloseKey(hKey);
		return FALSE;
	}
	g_bLoggingEnabled = bytBoolean != 0;

	if (g_bLoggingEnabled)
	{
		dwBufferSize = sizeof(strText);
		if (ERROR_SUCCESS != RegQueryValueExA(hKey, "Logging Directory", NULL, &dwStringType, (LPBYTE)strText, &dwBufferSize))
		{
			RegCloseKey(hKey);
			return FALSE;
		}
		if (!CreateLogFile(strText))
		{
			RegCloseKey(hKey);
			return FALSE;
		}

		dwBufferSize = sizeof(bytBoolean);
		if (ERROR_SUCCESS != RegQueryValueExA(hKey, "Log Details", NULL, &dwBinaryType, &bytBoolean, &dwBufferSize))
		{
			RegCloseKey(hKey);
			return FALSE;
		}
		g_bLogDetails = bytBoolean != 0;
	}

	dwBufferSize = sizeof(g_strDatabaseConnectionString);
	if (ERROR_SUCCESS != RegQueryValueExA(hKey, "Database Information", NULL, &dwStringType, (LPBYTE)g_strDatabaseConnectionString, &dwBufferSize))
	{
		RegCloseKey(hKey);
		return FALSE;
	}

	RegCloseKey(hKey);
	return TRUE;
}

BOOL IsStringsEqualIgnoreCase(LPCSTR strFirst, LPCSTR strSecond)
{
	char szFirst[1024], szSecond[1024];
	strcpy(szFirst, strFirst); strcpy(szSecond, strSecond);
	strupr(szFirst); strupr(szSecond);
	return strcmp(szFirst, szSecond) == 0;
}
